package org.afox.util;

import java.lang.reflect.*;
import java.util.*;

public class RFactory
{
	private static String basePackage = "";

	public static void setBasePackage(String aBase)
	{
		basePackage = aBase;
		if (!basePackage.endsWith("."))
			basePackage = basePackage + ".";
	}

	public static Object newInstance(String className, List parameters) throws InstantiationException
	{
		try
		{
			return newInstance(className, parameters.toArray());
		}
		catch(Exception x)
		{
			System.out.println("Exception:" + x);
			x.printStackTrace();
			throw new InstantiationException(x.getMessage());
		}
	}

	public static Object newInstance(String className, Object[] parameters) throws InstantiationException
	{
		try
		{
			return getConstructor(className, RHelper.getTypes(parameters)).newInstance(parameters);
		}
		catch(Exception x)
		{
			System.out.println("Exception:" + x);
			x.printStackTrace();
			throw new InstantiationException(x.getMessage());
		}
	}

	public static Object newInstance(Class aClass , List parameters) throws InstantiationException
	{
		try
		{
			return newInstance(aClass, parameters.toArray());
		}
		catch(Exception x)
		{
			System.out.println("Exception:" + x);
			x.printStackTrace();
			throw new InstantiationException(x.getMessage());
		}
	}

	public static Object newInstance(Class aClass, Object[] parameters) throws InstantiationException
	{
		try
		{
			return matchConstructor(aClass.getDeclaredConstructors(),RHelper.getTypes(parameters)).newInstance(parameters);
		}
		catch(Exception x)
		{
			System.out.println("Exception:" + x);
			x.printStackTrace();
			throw new InstantiationException(x.getMessage());
		}
	}

	private static Constructor getConstructor(String className, Class[] parmTypes) throws ClassNotFoundException,
											      NoSuchMethodException
	{
		Class theClass;
		try
		{
			theClass = Class.forName(className);
		}
		catch (ClassNotFoundException x)
		{
			try
			{
				theClass = Class.forName(basePackage + className);
			}
			catch(ClassNotFoundException y)
			{
				throw y;
			}
		}
		return matchConstructor(theClass.getDeclaredConstructors(), parmTypes);

//		return theClass.getConstructor(parmTypes);
	}

	private static Constructor matchConstructor(Constructor[] constructors, Class[] parmTypes) throws NoSuchMethodException
	{
		for (int i = 0; i< constructors.length; i++)
		{
			if (constructors[i].getParameterTypes().length == 0 && parmTypes.length ==0)
				return constructors[i];

			// check the number of parameters.  If they don't match, go on to next constructor
			if (constructors[i].getParameterTypes().length != parmTypes.length)
				continue;

			if (TypeMapper.matchParameters(constructors[i].getParameterTypes(), parmTypes))
				return constructors[i];

		}
		throw new NoSuchMethodException("Cannot find constructor to match:" + parmTypes);
	}
	       
}
